/*
   Copyright (C) 2007 Yen-Ju Chen <yjchenx gmail>

   This application is free software; you can redistribute it and/or 
   modify it under the terms of the MIT license. See COPYING.

*/

#import "KRWindowController.h"
#import "KRToolbar.h"

static NSMutableArray *controllers;

static int windowShiftX = 200; // Try to put window at random place 
static int windowShiftY = 200; // Try to put window at random place 

@implementation KRWindowController
/* Private */
- (OKGroup *) _selectedGroupInCollectionView
{
	if (isShowingCollectionView) 
	{
		int index = [collectionView selectedRow];
        if ((index == NSNotFound) || (index == -1))
        {
            return nil; /* No selection */
        }
		return [collectionView itemAtRow: index];
	}
	else
	{
		if (currentFactory)
			return [currentFactory rootGroup];
	}
	return nil;
}

- (void) _reloadItemData
{
	OKGroup *itemGroup = [self _selectedGroupInCollectionView];

	if (itemGroup == nil)
		return;

	if ([itemGroup isKindOfClass: [OKGroup class]] == NO)
		NSLog(@"Internal Error: %@ is not a OKGroup", itemGroup);

	[currentItemViewController setGroup: itemGroup];
	[currentItemViewController refresh];
}

- (void) _doubleClickAction: (id) sender
{
	if (sender == collectionView)
	{
		int index = [collectionView selectedRow];
		if ((index == NSNotFound) || (index == -1))
		{
			return; /* No selection */
		}
        
		/* Give it a chance to fill the subgroups */
		OKGroup *item = [currentFactory rootGroup];
		NSObject <KRWindowControllerProtocol> *controller = [KRWindowController windowControllerForObject: item];
		if (controller)
		{
			[controller showWindow: self];
		}
		else
		{
			controller = [[KRWindowController alloc] init];
			[controller setShowingCollectionView: NO];
			[controller setDataSource: dataSource];
			[controller setRootGroup: item];
			[controller showWindow: self];
			DESTROY(controller);
		}
	}
}

- (void) _rebuildWindow
{
	NSRect rect;
	if (window == nil)
	{
	    rect = NSMakeRect(windowShiftX, windowShiftY, 500, 500);
		window = [[NSWindow alloc] initWithContentRect: rect
		                           styleMask: NSTitledWindowMask |
		                                      NSClosableWindowMask |
		                                      NSMiniaturizableWindowMask |
#ifndef GNUSTEP
		                                      NSTexturedBackgroundWindowMask |
#endif
		                                      NSResizableWindowMask 
		                           backing: NSBackingStoreBuffered 
		                           defer: NO];
		[window setReleasedWhenClosed: NO];
		[window setDelegate: self];
		windowShiftX += random() & 0xff;
		windowShiftY -= random() & 0xff;
	}
	if (toolbar == nil)
	{
		rect = [[window contentView] bounds];
		rect.origin.y = rect.size.height - 30;
		rect.size.height = 30;
		toolbar = [[KRToolbar alloc] initWithFrame: rect];
		[toolbar setDelegate: self];
		[toolbar setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin];
		[[window contentView] addSubview: toolbar];
		
	}
	if (rightSplitView == nil)
	{
		rect = [[window contentView] bounds];
		rect.size.width -= 150;
		rightSplitView = [[NSSplitView alloc] initWithFrame: rect];
		[rightSplitView setAutoresizesSubviews: YES];
		[rightSplitView setAutoresizingMask: NSViewWidthSizable | 
		                                     NSViewHeightSizable];
		[rightSplitView setVertical: NO];
	}
	if (itemView == nil)
	{
		rect = [rightSplitView bounds];
		rect.size.height = rect.size.height / 3;
		itemView = [[NSView alloc] initWithFrame: rect];
		[itemView setAutoresizingMask: NSViewWidthSizable | 
		                               NSViewHeightSizable];
	}

	if (currentFactory)
	{
		unsigned int mask = [currentFactory viewableMask];
		if (mask & KRFactoryBrowserViewableMask)
		{
			if (browserView == nil)
			{
				NSLog(@"Build browser");
			}
		}
		else
		{
			if ([browserView superview])
				[browserView removeFromSuperview];
		}
		if (mask & KRFactoryItemViewableMask)
		{
			[rightSplitView addSubview: itemView];
		}
		else
		{
			if ([itemView superview])
				[itemView removeFromSuperview];
		}
		if (mask & KRFactoryContentViewableMask)
		{
			if (contentView == nil)
			{
				rect = [rightSplitView bounds];
				rect.size.height = rect.size.height * 2 / 3;
				contentView = [[NSView alloc] initWithFrame: rect];
				[contentView setAutoresizingMask: NSViewWidthSizable | 
				                                  NSViewHeightSizable];
			}
			[rightSplitView addSubview: contentView];
		}
		else
		{
			if ([contentView superview])
				[contentView removeFromSuperview];
		}
	}
	else
	{
		[rightSplitView addSubview: itemView];
	}

	if (isShowingCollectionView)
	{
		if (leftSplitView == nil)
		{
			rect = [[window contentView] bounds];
			leftSplitView = [[NSSplitView alloc] initWithFrame: rect];
			[leftSplitView setVertical: YES];
			[leftSplitView setAutoresizesSubviews: YES];
			[leftSplitView setAutoresizingMask: NSViewWidthSizable | 
		                                        NSViewHeightSizable];
		}
		if (collectionView == nil)
		{
			rect = [leftSplitView bounds];
			rect.size.width = 150;
			collectionScrollView = [[NSScrollView alloc] initWithFrame: rect];
			[collectionScrollView setAutoresizingMask: NSViewWidthSizable | 
		                                               NSViewHeightSizable];

			NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier: @"Collection"];
			[column setEditable: NO];

			collectionView = [[NSOutlineView alloc] initWithFrame: rect];
			[collectionView setDataSource: self];
			[collectionView setDelegate: self];
			[collectionView setTarget: self];
			[collectionView setDoubleAction: @selector(_doubleClickAction:)];
			[collectionView setAutoresizingMask: NSViewWidthSizable | 
		                                         NSViewHeightSizable];
			[collectionView setHeaderView: nil];
			[collectionView setCornerView: nil];
			[collectionView addTableColumn: column];
			[collectionView setOutlineTableColumn: column];

			[collectionScrollView setDocumentView: collectionView];
		}
		if ([leftSplitView superview] == nil)
		{
			if ([rightSplitView superview])
				[rightSplitView removeFromSuperview];
			/* Be sure we don't cover toolbar */
			rect = [[window contentView] bounds];
			rect.size.height -= ([toolbar bounds].size.height+30+5);
			rect.origin.y = 30;
			[leftSplitView setFrame: rect];
			[leftSplitView addSubview: collectionScrollView];
			[leftSplitView addSubview: rightSplitView];
			[[window contentView] addSubview: leftSplitView];
		}
	}
	else
	{
		if ([leftSplitView superview])
		{
			[leftSplitView removeFromSuperview];
		}
		if ([rightSplitView superview] == leftSplitView)
		{
			[rightSplitView removeFromSuperview];
		}
		
		/* Be sure we don't cover toolbar */
		rect = [[window contentView] bounds];
		rect.size.height -= ([toolbar bounds].size.height+30+5);
		rect.origin.y = 30;
		[rightSplitView setFrame: rect];
		[[window contentView] addSubview: rightSplitView];
	}
}

- (void) _updateCollectionMenu
{
	id <NSMenuItem > menuItem = [[NSApp mainMenu] itemWithTag: KRCollectionMenuItem];
	if (menuItem == nil)
	{
		NSLog(@"Internal Error: cannot find collection menu item");
	}
	NSMenu *submenu = [menuItem submenu];;
	if (submenu == nil)
	{
		NSLog(@"Internal Error: cannot find collection submenu");
	}
	/* Remove all menu items */
	while ([submenu numberOfItems])
	{
		[submenu removeItemAtIndex: 0];
	}
	if ([currentFactory respondsToSelector: @selector(collectionMenuItems)])
	{
		NSEnumerator *e = [[currentFactory collectionMenuItems] objectEnumerator];
		id <NSMenuItem> item = nil;
		while ((item = [e nextObject]))
		{
			[submenu addItem: item];
		}
	}
}

- (void) _setCurrentFactory: (NSObject <KRFactoryProtocol> *) factory
{
	if ((currentFactory == nil) ||
		(currentFactory != factory))
	{
		/* Remove previous subview */
		while ([[contentView subviews] count])
		{
			[[[contentView subviews] objectAtIndex: 0] removeFromSuperview];
		}

		currentFactory = factory;
		[self _rebuildWindow];
		if ([currentFactory respondsToSelector: @selector(actionMenu)])
		{
			[toolbar setActionMenu: [currentFactory actionMenu]];
		}
		else
		{
			[toolbar setActionMenu: nil];
		}

		unsigned int mask = [currentFactory viewableMask];
		if ((mask & KRFactoryContentViewableMask) &&
			([currentFactory respondsToSelector: @selector(contentView)]))
		{
			NSView *view = [currentFactory contentView];
			if (view)
			{
				[view setFrame: [contentView bounds]];
				[contentView addSubview: view];
			}
		}
	}
	if ([window isMainWindow])
		[self _updateCollectionMenu];
}

- (void) _factoryDidUpdate: (NSNotification *) not
{
	if ([not object] == currentFactory)
	{
		[collectionView reloadData];
		[self _reloadItemData];
	}
}

/* End of Private */

+ (NSArray *) allWindowControllers
{
	return controllers;
}

+ (NSObject <KRWindowControllerProtocol> *) windowControllerForObject: (OKObject *) object
{
	int i, count = [controllers count];
	KRWindowController *controller = nil;
	for (i = 0; i < count; i++)
	{
		controller = [controllers objectAtIndex: i];
		if ([[controller rootGroup] isEqual: object])
			return controller;
	}
	return nil;
}

- (void) awakeFromNib
{
	/* Get all item view controller; */
	BKPluginRegistry *pluginRegistry = [BKPluginRegistry sharedInstance];
	ASSIGN(itemViewControllers, [pluginRegistry loadedValidOrderedExtensionsFor:@"org.etoile-project.koelr.windowcontroller.itemview" protocol:@protocol(KRItemViewControllerProtocol)]);
	if ([itemViewControllers count] < 1)
	{
		NSLog(@"Internal Error: no item view");
		[NSApp terminate: nil];
	}

	[self setItemViewController: [[itemViewControllers objectAtIndex: 0] extensionNewInstance]];
}

- (void) windowDidBecomeKey: (NSNotification *) not
{
	[self _updateCollectionMenu];
}

- (IBAction) showWindow: (id) sender
{
	if (window == nil)
	{
		NSObject <KRFactoryProtocol> *factory = nil;
		if (isShowingCollectionView == NO)
		{
			/* If window is shown at the first time with collection view,
			   set current factory to root group */
		    factory = [dataSource factoryForGroup: rootGroup];
			if ([factory respondsToSelector: @selector(willDisplayGroup:)])
			{
				[factory willDisplayGroup: rootGroup];
		    }
		}
		[self _setCurrentFactory: factory];
		if (window == nil)
		{
			NSLog(@"Failed to make window");
			return;
		}
		[self awakeFromNib];
		[self _reloadItemData];
	}

	[window makeKeyAndOrderFront: self];
}

- (void) setDataSource: (NSObject <KRDataSourceProtocol> *) ds
{
	ASSIGN(dataSource, ds);
}

- (void) setRootGroup: (OKGroup *) group
{
	ASSIGN(rootGroup, group);
}

- (OKGroup *) rootGroup
{
	return rootGroup;
}

- (void) setPredicate: (NSPredicate *) p
{
	ASSIGN(predicate, p);
	[currentItemViewController setPredicate: predicate];
}

- (NSPredicate *) predicate
{
	return predicate;
}

- (void) setItemViewController: (NSObject <KRItemViewControllerProtocol> *) controller
{
	if (currentItemViewController != controller)
	{
		/* Remove previous subview */
		while ([[itemView subviews] count])
		{
			[[[itemView subviews] objectAtIndex: 0] removeFromSuperview];
		}
		ASSIGN(currentItemViewController, controller);
		[currentItemViewController setDataSource: dataSource];

		NSRect rect = [itemView bounds];
		NSView *itemSubview = [controller itemView];
		[itemSubview setFrame: rect];
		[itemView addSubview: itemSubview];
	}
}

- (void) setShowingCollectionView: (BOOL) flag
{
	isShowingCollectionView = flag;
}

- (BOOL) isShowingCollectionView
{
	return isShowingCollectionView;
}

/* KRToolbar */
- (void) toolbar: (KRToolbar *) tb
         searchPredicateDidChange: (NSPredicate *) p
{
	[self setPredicate: p];
}

/* Outline View */
- (BOOL) outlineView: (NSOutlineView *) outlineView 
         shouldExpandItem: (id) item
{
	return YES;
}

- (void)outlineViewItemWillExpand: (NSNotification *) not
{
	OKGroup *group = [[not userInfo] objectForKey: @"NSObject"];
    NSObject <KRFactoryProtocol> *factory = [dataSource factoryForGroup: group];
	if ([factory respondsToSelector: @selector(willDisplayGroup:)])
	{
		[factory willDisplayGroup: group];
	}
}

- (BOOL) outlineView:(NSOutlineView *) outlineView 
         shouldSelectItem: (id) item
{
	return YES;
}

- (void)outlineViewSelectionDidChange: (NSNotification *) not
{
	int index = [collectionView selectedRow];
	if ((index == NSNotFound) || (index == -1))
	{
		/* No selection */
		return;
	}

	/* Give it a chance to fill the subgroups */
	OKGroup *itemGroup = [collectionView itemAtRow: index];

	if ([itemGroup isKindOfClass: [OKGroup class]] == NO)
		NSLog(@"Internal Error: %@ is not a OKGroup", itemGroup);
	
    NSObject <KRFactoryProtocol> *factory = [dataSource factoryForGroup: itemGroup];
	if ([factory respondsToSelector: @selector(willDisplayGroup:)])
	{
		[factory willDisplayGroup: itemGroup];
    }
	[self _setCurrentFactory: factory];
	[self _reloadItemData];

}

- (int) outlineView: (NSOutlineView *) ov
        numberOfChildrenOfItem: (id) item
{
	if (item == nil)
	{
		return [[dataSource factories] count];
	}
	else if ([item isKindOfClass: [OKGroup class]])
	{
		return [[(OKGroup *)item subgroups] count];
	}
	return 0;
}

- (id) outlineView: (NSOutlineView *) ov
       child: (int) index ofItem: (id) item
{
	OKGroup *group = nil;

	if (item == nil)
	{
		group = [[[dataSource factories] objectAtIndex: index] rootGroup];
	}
	else if ([item isKindOfClass: [OKGroup class]])
	{
		group = [[(OKGroup *)item subgroups] objectAtIndex: index];
	}

	return group;
}


- (id) outlineView: (NSOutlineView *) ov 
       objectValueForTableColumn: (NSTableColumn *) tc byItem: (id) item
{
	if ([item isKindOfClass: [OKGroup class]])
	{
		return [(OKGroup *)item valueForProperty: kOKGroupNameProperty];
	}
	return nil;
}

- (BOOL) outlineView: (NSOutlineView *) ov
         isItemExpandable: (id) item
{
	if ([item isKindOfClass: [OKGroup class]])
	{
		/* We only show up to 2nd level */
		NSObject <KRFactoryProtocol> *factory = [dataSource factoryForGroup: item];
		if ([[factory rootGroup] isEqual: item])
		{
			if ([[item subgroups] count])
				return YES;
		}
	}
	return NO;
}

/* NSObject */
- (id) init
{
	self = [super init];
	nc = [NSNotificationCenter defaultCenter];
	if (controllers == nil)
	{
		controllers = [[NSMutableArray alloc] init];
	}
	[controllers addObject: self];
	isShowingCollectionView = YES;
	[nc addObserver: self
	    selector: @selector(_factoryDidUpdate:)
	    name: KRFactoryDidUpdateNotification
	    object: nil];
	return self;
}

- (void) dealloc
{
	[controllers removeObject: self];
	DESTROY(toolbar);
	DESTROY(browserView);
	DESTROY(itemView);
	DESTROY(contentView);
	DESTROY(collectionView);
	DESTROY(rightSplitView);
	DESTROY(leftSplitView);
	[super dealloc];
}

@end

